import time
import pickle
import os
import sys
import threading
import Queue

from whoisparser import EntryUtil
from util import ip2num
from query import NICClient

class WhoisCache(object):
    def __init__(self,cache_file='whois.dat'):
        self.cache_file = cache_file
        self.load()
        self.clearExpired()
        
    def load(self):
        if os.path.isfile(self.cache_file):
            self.data = pickle.load(open(self.cache_file))
            self._initKeys()
        else:
            self.data = {}

        if len(self.data) ==0:
            self._initData()
        
    def _initKeys(self):
        self.keys = self.data.keys()
        self.keys.sort()
        
    def _initData(self):
        #always ignores the following special IP address blocks, because they are not used as public Internet addresses:
        lan =(('10.0.0.0','10.255.255.255'),
              ('127.0.0.0','127.255.255.255'),
              ('169.254.0.0','169.254.255.255'),
              ('172.16.0.0','172.31.255.255'),
              ('192.168.0.0','192.168.255.255'),
              ('224.0.0.0','239.255.255.255'),
              )
        expired = time.time() + 10 * 365 * 24 * 60 * 60
        for k in lan:
            self.data[(ip2num(k[0]), ip2num(k[1]))] = ('','',expired)
        self._initKeys()
    
    def save(self,saveas=None):
        if not saveas:
            saveas = self.cache_file
        pickle.dump(self.data, open(saveas,'w'))
    
    def expiredTime(self):
        return time.time() - 90*24*60*60
    
    def clearExpired(self):
        expired = self.expiredTime()
        self.data = dict([(k,v) for (k,v) in self.data.iteritems() if v[2] >= expired])
    
    def _halfSearch(self, find):  
        low = 0   
        high = len(self.keys) - 1  
        while low<=high:  
            mid = (low + high)/2  
            if self.keys[mid][0]<=find and find <= self.keys[mid][1]:  
                return self.keys[mid] 
            else:  
                if find > self.keys[mid][1]:  
                    low = mid + 1 
                else:
                    high = mid - 1  
        return None  
    
    def findOne(self,ip):
        if len(self.keys) == 0:
            return None
        
        ipnum = ip2num(ip)
        if ipnum < self.keys[0][0] or ipnum > self.keys[-1][1]:
            return None
        
        key = self._halfSearch(ipnum)
        if key is not None and self.data[key][2] >= self.expiredTime():
            return self.data[key][:2]
        return None
    
    def country(self,ip):
        return self._find(ip,0)
    
    def isp(self,ip):
        return self._find(ip,1)
    
    def _find(self,ip,index):
        d = self.findOne(ip)
        if d is not None:
            return d[index]
        return ''
    #data:((network_start_num, network_end_num),(country, isp)) 
    def append(self, data):
        k = data[0]
        v = data[1]
        if k != None:
            self.data[k] = v + (time.time(),)
            self.save()
            self._initKeys()
            return v
        return None
    
    def clear(self):
        self.data.clear()
        self._initData()
#--END OF WhoisCache class def -------------------------------------------------------------

class WhoisCacheSync(WhoisCache):
    entry = EntryUtil()
    
    def __init__(self,cache_file='whois.dat'):
        WhoisCache.__init__(self, cache_file)
        
    def findOne(self, ip):
        res = WhoisCache.findOne(self, ip)
        if res == None:
            nic_client = NICClient()
            texts = nic_client.whois_lookup(None, ip, 0)
            data = self.entry.loads(texts)
            
            if data is not None:
                res = self.append(data)
        return res
#--END OF WhoisCacheSync class def -------------------------------------------------------------

class ThreadQuery(threading.Thread):
    entry = EntryUtil()
    
    def __init__(self, cache):
        threading.Thread.__init__(self)
        self.cache = cache
        self.queue = cache.queue
          
    def run(self):
        while True:
            #grabs ip from queue
            ip = self.queue.get()
            try:
                nic_client = NICClient()
                texts = nic_client.whois_lookup(None, ip, 0)
                data = self.entry.loads(texts)
                
                if data is not None:
                    self.cache.append(data)

            except:
                print >>sys.stderr, 'query %s error!' % ip
            
            finally:                
                #signals to queue job is done
                self.queue.task_done()
                    
class WhoisCacheThread(WhoisCache):
    def __init__(self, cache_file='whois.dat', thread_num = 5):
        WhoisCache.__init__(self, cache_file)
        self.queue = Queue.Queue()
        self.threads = []
        for i in range(thread_num):
            t = ThreadQuery(self)
            t.setDaemon(True)
            t.start() 
            self.threads.append(t) #??
        
    def findOne(self, ip):
        res = WhoisCache.findOne(self, ip)
        
        if res == None:
            self.queue.put(ip)
            
        return res
    
    def join(self):
        self.queue.join()

#--END OF WhoisCacheThread class def -------------------------------------------------------------